import axios from 'axios';
import io from 'socket.io-client';
import JsSIP from 'jssip';
import { CommandEnum } from './const/command';
import { ErrorCodeEnum, ErrorCodeMap } from './const/index';
import { SeatStateEnum, SeatStateMap, StatusEnum } from './const/state';
import { isFunction } from './utils';

export default class Actions {
  socket;

  sipUA;

  rtcSession = null; // 当前通话

  callTime = 0; // 来电时间 or 拨打时间

  connectTime = 0; // 接通时间

  // 当前坐席状态
  seat = {
    state: 'NotReady',
    timestamp: null,
  };

  constructor(options, events) {
    this.options = options;
    this.events = events;
    this.data = null;
  }

  get duration() {
    const start = this.seat.timestamp || 0;
    return (Date.now() - start);
  }

  set duration(timestamp) {
    this.seat.timestamp = timestamp;
  }

  genError(code, message) {
    const name = ErrorCodeMap[code];
    const { error } = this.options;
    if (isFunction(error)) {
      error({
        name,
        message,
        code,
        type: 'Permission', // TODO: 应该有其他类型？
      });
    }
  }

  initWebsocket(url) {
    try {
      this.socket = io.connect(url, {
        transports: ['websocket'],
        forceNew: true,
      });
      this.emitCommand(CommandEnum.login);

      this.socket.on('connect', (data) => {
        console.info('连接成功', data);
      });
      this.socket.on('disconnect', (data) => {
        console.info('连接断开', data);
      });
      this.socket.on('error', (error) => {
        this.genError(ErrorCodeEnum.websocketError, error.message);
      });
      this.socket.on('status', (data) => {
        const { status, rest } = data;
        if (data != null) {
          const SourceState = this.seat.state; // 原状态
          const ToState = rest ? SeatStateEnum.Rest : (StatusEnum[status] || SourceState);
          // TODO: 如果当前是 Ready, 然后切换到吃饭，那么此时状态应该是什么？
          // console.log('emit command 返回的状态：', status);
          this.events.EventState({
            Result: true,
            ResultMsg: {
              SourceState, // 源状态
              ToState, // 变更后的状态
              rest, // 因旧demo，是否空闲是通过这个字段判断的，不暴露出去的话外面无法使用
              ErrorMsg: '',
            },
          });
          this.seat.state = ToState;

          if (data.name === 'CHANEL_CREATE') {
            this.events.EventMakeCall({ Result: true, ResultMsg: { CallId: data.id } });
          }
        }
      });
      this.socket.on(CommandEnum.login, (data) => {
        if (data.ok) {
          this.events.EventLogin({
            Result: true,
            ResultMsg: data,
            socket: this.socket,
            sipUA: this.sipUA,
          });
          this.selChange(SeatStateEnum.Ready);
          this.duration = Date.now();
        } else {
          this.events.EventLogin({ result: false, resultMsg: '登录失败' });
          this.selChange(SeatStateEnum.NotReady);
          this.duration = Date.now();
        }
      });
    } catch (e) {
      this.genError(ErrorCodeEnum.websocketInitFailed, e.message);
    }
  }

  initSip(webrtchost, webrtcport, waiterNo, password) {
    const url = `wss://${webrtchost}:${webrtcport}`;
    const sipUrl = `sip:${waiterNo}@${webrtchost};transport=wss`;
    const socket = new JsSIP.WebSocketInterface(url);
    const configuration = {
      sockets: [socket],
      outbound_proxy_set: sipUrl,
      uri: sipUrl,
      password,
      display_name: waiterNo,
      register: true,
      no_answer_timeout: 600,
      register_expires: 300,
      contact_uri: sipUrl,
      session_timers: false, // 启用会话定时器,默认值是true。
      user_agent: 'UCKeFu-SoftPhone WebRTC v5.0',
    };
    const UA = new JsSIP.UA(configuration);
    UA.start();

    UA.on('registered', () => {
      this.events.EventRegister({
        Result: true,
        ResultMsg: 'RegisterSuccess',
      });
    });

    UA.on('unregistered', () => {
      this.events.EventRegister({
        Result: false,
        ResultMsg: '"RegisterFail',
      });
    });

    UA.on('newRTCSession', this.newRTCSession);
    return UA;
  }

  newRTCSession({ session }) {
    console.log('session--', session);
    this.rtcSession = session;
    session.on('accepted', ({ response }) => {
      // 呼出的电话被对方接通时触发
      this.events.EventCallIng({
        Result: true,
        ResultMsg: {
          CallId: response.call_id, // TODO：还需要返回录音名和地址 RedURL, CtiDialogId 会话id
          CtiDialogId: '',
          RedURL: '',
        },
      });
    });

    session.on('confirmed', ({ originator }) => {
      // 确认对方已接通时触发
      this.connectTime = Date.now();
      console.log('accepted confirmed====>', originator);
    });

    session.on('ended', ({ originator, message }) => {
      const EndTime = Date.now();
      // 通话结束 originator 'local/remote/system'( 本地 | 对方 | 系统 ) 挂断
      const SignalIngMap = { // 挂断方 2: 坐席，1:用户
        local: 2,
        remote: 1,
      };
      this.events.EventHangup({
        Result: true,
        ResultMsg: {
          CallId: message.call_id,
          CtiDialogId: '', // 会话id
          CallTime: this.callTime, // 来电时间 or 拨打时间
          ConnectTime: this.connectTime, // 接通时间
          EndTime, // 挂断时间
          SignalIng: SignalIngMap[originator],
          WavFile: '', // 录音文件名
        },
      });
    });
  }

  emitCommand(command, data, restreason) {
    if (this.socket == null) console.error('请先实例化 websocket');
    this.socket.emit('command', { command, data: data || this.data, restreason });
  }

  // 2.4 坐席账号集成登录方法
  // eslint-disable-next-line
  async loginbytoken(waiterNo, waiterId, outerAccountName, token, tenantId, companyId) {
    const { callserverurl } = this.options;
    // TODO: waiterId, outerAccountName, companyId 目前没用到
    // TODO: 需要去掉路径和除token外的参数
    const res = await axios({ url: `${callserverurl}/apps/callcenter/extention/login.html?extno=${waiterNo}&orgi=${tenantId}&password=${token}` });
    const { status, data, statusText } = res;
    if (status === 200) {
      const { webrtchost, webrtcport, userid } = data;
      this.data = data;
      // TODO: 该 host 和 port 部分待确认从哪里获取
      this.initWebsocket(`${callserverurl}${webrtcport ? ':9081' : ''}/callcenter/event?orgi=${tenantId}&extention=${waiterNo}&userid=${userid}&organ=`);
      this.sipUA = this.initSip(webrtchost, webrtcport, waiterNo, token);
    } else {
      // TODO: 登录异常是指这个？
      this.genError(ErrorCodeEnum.loginError, statusText);
    }
  }

  // 2.4.1 检查是否连接
  checkConn() {
    return this.sipUA.isConnected();
  }

  // 2.5 获取坐席状态方法
  getAgentState() {
    const State = this.seat.state;
    const Duration = this.duration;
    this.events.EventGetState({
      Result: true,
      ResultMsg: {
        State,
        Duration,
        ErrorMsg: '',
      },
    });
  }

  // 2.6 坐席状态切换方法
  selChange(state) {
    const SourceState = this.seat.state;
    try {
      switch (state) {
        case SeatStateEnum[state]:
          this.emitCommand(SeatStateMap[state].command, this.data, SeatStateMap[state].reason);
          break;
        case 'Leave':
          this.emitCommand('rest', this.data, 'Leave');
          break;
        default:
          break;
      }
      this.duration = Date.now();
    } catch (e) {
      this.events.EventState({
        Result: false,
        ResultMsg: {
          SourceState,
          ToState: state,
          ErrorMsg: e.message,
        },
      });
    }
  }

  /**
   * 2.7 外呼方法
   * @param param - {
   *  zcaller: string; phone: string; taskId: string; taskDetailId?: string; business?: string;
   *  hasencrypt: boolean; type: 0 | 1; IEID?: string; extension?: string;
   * }
   */
  call(param) {
    try {
      const {
        phone, zcaller, taskId, taskDetailId, hasencrypt, type, IEID, extension,
      } = JSON.parse(param);
      const calledPhone = String(phone).trim().replace('-', '').replace(/[^0-9]/g, '');
      const zcallerHeader = `X-Extphonw: ${zcaller}`;
      const busCallHeader = `X-Dataid: ${taskId}, X-Conid: ${taskDetailId}`;
      const extraHeader = `X-hasencrypt: ${hasencrypt}, X-type: ${type}, X-IEID: ${IEID}, X-extension: ${extension}`;

      if (this.sipUA) {
        this.sipUA.call(calledPhone, {
          mediaConstraints: {
            audio: true,
            video: false,
          },
          mediaStream: null,
          extraHeaders: [
            'X-statustype: contacts',
            `${taskId && taskDetailId ? busCallHeader : ''}${zcaller ? zcallerHeader : ''}${extraHeader}`,
          ],
        });
        this.callTime = Date.now();
      }
    } catch (e) {
      console.error(e.message);
    }
  }

  docallout() {
    this.emitCommand('docallout');
  }

  // // 2.8 通话按键方法
  // sendDtmf() {},
  // 2.9 接听方法
  answer() {
    this.rtcSession.answer();
  }

  // 2.10 挂断方法
  hangup() {
    this.rtcSession.terminate();
  }

  // // 2.11 保持方法
  // holdcall() {},
  // // 2.12 取消保持方法
  // reholdcall() {},
  // // 2.13 获取转 IVR 流程列表方法
  // getivrlist() {},
  // // 2.14 转 IVR 流程方法
  // toivr() {},
  // // 2.15 获取磋商队列方法
  // getconsultlist() {},
  // // 2.16 磋商方法
  // consult() {},
  // // 2.17 接回方法
  // transferme() {},
  // // 2.18 转接方法
  // transfer() {},
  // // 2.19 会议方法
  // conference() {},
  // // 2.20 话后提交方法
  // HHcommit() {},
  // // 2.21 状态复位方法（前端实现）
  // setConfig() {},
  // 2.22 注销退出方法
  logOut() {
    this.emitCommand(CommandEnum.logout);
    this.socket.disconnect();
    this.socket = null;

    this.events.EventLogOut({
      Result: true,
      ResultMsg: '',
    });
  }
}
